home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Tools & Utilities
/
Collection of Tools and Utilities.iso
/
edit
/
xvisrc.zip
/
UNDO.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-28
|
21KB
|
879 lines
/* Copyright (c) 1990,1991,1992 Chris and John Downey */
#ifndef lint
static char *sccsid = "@(#)undo.c 2.2 (Chris & John Downey) 8/28/92";
#endif
/***
* program name:
xvi
* function:
PD version of UNIX "vi" editor, with extensions.
* module name:
undo.c
* module function:
Code to implement "undo" command.
* usage:
We provide several primitive functions for "do"ing things,
and an "undo" function to restore the previous state.
Normally, a primitive function is simply called, and will
automatically throw away the old previous state before
saving the current one and then making the change.
Alternatively, it is possible to bracket lots of changes
between calls to the start_command() and end_command()
functions; more global changes can then be effected,
and undo still works as it should. This is used for the
"global" command, for multi-line substitutes, and for
insert mode.
* history:
STEVIE - ST Editor for VI Enthusiasts, Version 3.10
Originally by Tim Thompson (twitch!tjt)
Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
Heavily modified by Chris & John Downey
***/
#include "xvi.h"
static void save_position P((Xviwin *));
static void free_changes P((Change *));
static void report P((Xviwin *));
/*
* This variable holds the total number of added/deleted lines
* for a change; it is used for reporting (the "report" parameter).
*/
static long total_lines;
void
init_undo(buffer)
Buffer *buffer;
{
/*
* Initialise the undo-related variables in the Buffer.
*/
buffer->b_nlevels = 0;
buffer->b_change = NULL;
/*
* Set line numbers.
*/
buffer->b_line0->l_number = 0;
buffer->b_file->l_number = 1;
buffer->b_lastline->l_number = MAX_LINENO;
}
/*
* Start a command. This routine may be called many times;
* what it does is increase the variable which shows how
* many times it has been called. The end_command() then
* decrements the variable - so it is vital that calls
* of the two routines are matched.
*
* The effect of this is quite simple; if the nlevels variable
* is >0, the "do*" routines will not throw away the previous
* state before making a change; and thus we are able to undo
* multiple changes.
*
* All the do* routines, and start_command(), will throw away
* the previous saved state if the b_nlevels variable is 0.
*/
bool_t
start_command(window)
Xviwin *window;
{
Buffer *buffer;
buffer = window->w_buffer;
if (buffer->b_nlevels == 0) {
if (not_editable(buffer)) {
show_error(window, "Edit not allowed!");
return(FALSE);
}
free_changes(buffer->b_change);
buffer->b_change = NULL;
}
buffer->b_nlevels += 1;
total_lines = 0;
save_position(window);
return(TRUE);
}
/*
* Save the cursor position.
*
* This is called at the start of each change, so that
* the cursor will return to the right place after an undo.
*/
static void
save_position(window)
Xviwin *window;
{
Buffer *buffer;
Change *change;
buffer = window->w_buffer;
change = challoc();
if (change == NULL)
return;
change->c_type = C_POSITION;
change->c_pline = lineno(buffer, window->w_cursor->p_line);
change->c_pindex = window->w_cursor->p_index;
change->c_next = buffer->b_change;
buffer->b_change = change;
}
void
end_command(window)
Xviwin *window;
{
Buffer *buffer;
buffer = window->w_buffer;
if (buffer->b_nlevels > 0) {
buffer->b_nlevels -= 1;
if (buffer->b_nlevels == 0) {
report(window);
}
} else {
show_error(window, "Internal error: too many \"end_command\"s");
}
}
/*
* Replace the given section of the given line with the
* new (null-terminated) string. nchars may be zero for
* insertions of text; newstring may point to a 0-length
* string to delete text. start is the first character
* of the section which is to be replaced.
*
* Note that we don't call strcpy() to copy text here, for
* two reasons: firstly, we are likely to be copying small
* numbers of characters and it is therefore faster to do
* the copying ourselves, and secondly, strcpy() doesn't
* necessarily work for copying overlapping text towards
* higher locations in memory.
*/
void
replchars(window, line, start, nchars, newstring)
Xviwin *window;
Line *line;
int start;
int nchars;
char *newstring;
{
register char *from; /* where to copy from */
register char *to; /* where to copy to */
register int nlen; /* length of newstring */
register int olen; /* length of old line */
register int offset; /* how much to move text by */
Buffer *buffer;
Change *change;
buffer = window->w_buffer;
/*
* If this is a singleton command, make sure we
* destroy the changes made as part of the last
* command before we start the new one.
*/
if (buffer->b_nlevels == 0) {
if (not_editable(buffer)) {
show_error(window, "Edit not allowed!");
return;
}
free_changes(buffer->b_change);
buffer->b_change = NULL;
save_position(window);
}
/*
* First thing we have to do is to obtain a change
* structure to record the change so we can be sure
* that we can undo it. If this fails, we must not
* make any change at all.
*/
change = challoc();
if (change == NULL)
return;
nlen = strlen(newstring);
olen = strlen(line->l_text + start);
if (olen < nchars)
nchars = olen;
offset = nlen - nchars;
/*
* Record the opposite change so we can undo it.
*/
if (nchars == 0) {
change->c_type = C_DEL_CHAR;
} else {
change->c_type = C_CHAR;
change->c_chars = alloc((unsigned) nchars + 1);
if (change->c_chars == NULL) {
chfree(change);
State = NORMAL;
return;
}
(void) strncpy(change->c_chars, line->l_text + start, nchars);
change->c_chars[nchars] = '\0';
}
change->c_lineno = lineno(buffer, line);
change->c_index = start;
change->c_nchars = nlen;
if (offset > 0) {
register char *s;
/*
* Move existing text along by offset to the right.
* First make some room in the line.
*/
if (grow_line(line, offset) == FALSE) {
free(change->c_chars);
chfree(change);
State = NORMAL;
return;
}
/*
* Copy characters backwards, i.e. start
* at the end of the line rather than at
* the start.
*/
from = line->l_text + start;
to = from + offset + olen + 1;
s = from + olen + 1;
while (s > from) {
*--to = *--s;
}
} else if (offset < 0) {
/*
* Move existing text along to the left.
*/
offset = - offset;
to = line->l_text + start;
from = to + offset;
/*
* Do classic K&R strcpy().
*/
while ((*to++ = *from++) != '\0') {
;
}
}
/*
* Finally, copy the new text into position.
* Note that we are careful not to copy the
* null terminator.
*/
from = newstring;
to = line->l_text + start;
while (*from != '\0') {
*to++ = *from++;
}
buffer->b_flags |= FL_MODIFIED;
window->w_curs_new = TRUE;
/*
* Push this change onto the LIFO of changes
* that form the current command.
*/
change->c_next = buffer->b_change;
buffer->b_change = change;
}
/*
* Replace the specified set of lines with the replacement set.
* The number of lines to be replaced may be 0; the replacement
* list may be a NULL pointer.
*/
void
repllines(window, line, nolines, newlines)
register Xviwin *window;
Line *line;
long nolines;
Line *newlines;
{
register Buffer *buffer; /* buffer window is mapped onto */
Line *firstp; /* line before first to delete */
Line *lastp; /* line after last to delete */
Line *lastline; /* last line to delete */
Line *new_start; /* start of lines to be inserted */
Line *new_end; /* last line to be inserted */
long nnlines; /* no. new logical lines */
long oplines; /* no. physical lines to be replaced */
long nplines; /* no. new physical lines */
long n; /* lines done so far */
register Xviwin *wp; /* loop variable */
Change *change;
buffer = window->w_buffer;
/*
* If this is a singleton com